using System;
using NUnit.Framework;
using OpenRay;
using System.Collections.Generic;

using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;

namespace Tests
{
	[TestFixture]
	public class TestSphere
	{
		private void SavePNG( FrameBuffer fb, string s ) {
			Bitmap bmp = new Bitmap(fb.Width, fb.Height );
			
			for ( int y = 0; y < fb.Height; y++ )
			{
				for ( int x = 0; x < fb.Width; x++ )
				{
					float[] color = fb.GetArgb(x,y);
					
					for( int i = 0; i < 4; i++ )
					{
						color[i] = (float)Math.Max( 0, Math.Min( 1f, color[i] )  );	
					}
					
					bmp.SetPixel(x, y, System.Drawing.Color.FromArgb( 255, (int)(color[1] * 255f), (int)(color[2] * 255f), (int)(color[3] * 255f) ) );
				}
			}
			
			bmp.Save(s, ImageFormat.Png );
			bmp.Dispose();
		}
		
		private OpenRay.PhongMaterial CreateMaterial( OpenRay.Color d )
		{
			OpenRay.Color[] ambient = new OpenRay.Color[2];
			OpenRay.Color[] diffuse = new OpenRay.Color[2];
			OpenRay.Color[] specular = new OpenRay.Color[2];
			OpenRay.Color[] emissive = new OpenRay.Color[2];
			float[] shininess = new float[2];
			
			ambient[0] = new OpenRay.Color( 0.2f, 0.2f, 0.2f, 1f );
			ambient[1] = ambient[0];
			
			diffuse[0] = d;
			diffuse[1] = d;
			
			specular[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			specular[1] = specular[0];
			
			emissive[0] = new OpenRay.Color( 0f, 0f, 0f, 1f );
			emissive[1] = emissive[0];
			
			shininess[0] = 10f;
			shininess[1] = shininess[0];
			
			return new OpenRay.PhongMaterial(ambient, diffuse, specular, emissive, shininess );
		}
		
		private OpenRay.PhongMaterial CreateReflectiveMaterial( OpenRay.Color d, IScene s )
		{
			OpenRay.Color[] ambient = new OpenRay.Color[2];
			OpenRay.Color[] diffuse = new OpenRay.Color[2];
			OpenRay.Color[] specular = new OpenRay.Color[2];
			OpenRay.Color[] emissive = new OpenRay.Color[2];
			OpenRay.Color[] reflective = new OpenRay.Color[2];
			float[] shininess = new float[2];
			
			ambient[0] = new OpenRay.Color( 0.2f, 0.2f, 0.2f, 1f );
			ambient[1] = ambient[0];
			
			diffuse[0] = d;
			diffuse[1] = d;
			
			specular[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			specular[1] = specular[0];
			
			emissive[0] = new OpenRay.Color( 0f, 0f, 0f, 1f );
			emissive[1] = emissive[0];
			
			shininess[0] = 10f;
			shininess[1] = shininess[0];
			
			reflective[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			reflective[1] = reflective[0];
			
			return new OpenRay.ReflectivePhongMaterial(ambient, diffuse, specular, emissive, shininess, reflective, s );
		}
		
		private List<OpenRay.Light> CreateLighting(OpenRay.Color d)
		{
			List<OpenRay.Light> ret = new List<OpenRay.Light>();
			
			OpenRay.Color dim = new OpenRay.Color(0.2f, 0.2f, 0.2f, 1f );
			OpenRay.Color white = new OpenRay.Color(1f, 1f, 1f, 1f);
			OpenRay.Color black = new OpenRay.Color(0f, 0f, 0f, 1f);
			
			ret.Add( new OpenRay.DirectionalPhongLight( dim, black, black, new Vector( 1f, 0f, 0f ), null ) );
			ret.Add( new OpenRay.DirectionalPhongLight( black, d, white, new Vector( -1f, 1f, 1f ), null ) );
			
			return ret;
		}
		
		[Test]
		public void TestColorSphereRender ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[1] { new OpenRay.Point( 0f, 0f, 0f, 1f ) };			
			Material[] m = new Material[1] { new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) ) };
			
			Geometry geom = new SphereGeometry( p, 0.5f, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, new List<OpenRay.Light>() );
			
			s.AddPrimitive( prim );
			
			OpenGLCamera camera = new OpenGLCamera(s, Matrix.Identity, 500, 500 );
			
			SavePNG( camera.Render(), "TestColorSphere.png" );
		}
		
		[Test]
		public void TestPhongSphereRender ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[1] { new OpenRay.Point( 0f, 0f, 0f, 1f ) };
			Material[] m = new Material[1] { CreateMaterial( new OpenRay.Color( 0.8f, 0.8f, 0.8f, 1f ) ) };
			Geometry geom = new SphereGeometry( p, 1f, m );
			
			Matrix translate = Matrix.CreateTranslate(0f, 0f, -2.1f);
			Matrix invTrans = Matrix.Inverse( translate );
			Primitive prim = new Primitive( translate, invTrans, geom, CreateLighting(new OpenRay.Color(1f, 0f, 0f, 1f) ) );
			
			s.AddPrimitive( prim );
			
			Matrix c2w = Matrix.Inverse( Matrix.CreateFrustum(-1f, 1f, -1f, 1f, 1f, 2f) );
			OpenGLCamera camera = new OpenGLCamera(s, c2w, 500, 500 );
			
			SavePNG( camera.Render(), "TestPhongSphere.png" );
		}
		
		[Test]
		public void TestReflectiveSphereRender ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[1] { new OpenRay.Point( 0f, 0f, 0f, 1f ) };
			OpenRay.Material[] m = new Material[1] { CreateReflectiveMaterial( new OpenRay.Color( 0.0f, 0.0f, 0.8f, 1f ), s ) };
			Geometry geom = new SphereGeometry( p, 1f, m );
			
			Matrix translate = Matrix.CreateTranslate(-1f, 0f, -3f);
			Matrix invTrans = Matrix.Inverse( translate );
			Primitive prim = new Primitive( translate, invTrans, geom, CreateLighting(new OpenRay.Color(0.8f, 0.8f, 0.8f, 1f) ) );
			
			s.AddPrimitive( prim );
			
			//----
			
			geom = new SphereGeometry( p, 1f, new Material[1] {  CreateReflectiveMaterial( new OpenRay.Color( 0.8f, 0.0f, 0.0f, 1f ), s ) } );
			
			translate = Matrix.CreateTranslate(1f, 0f, -3f);
			invTrans = Matrix.Inverse( translate );
			prim = new Primitive( translate, invTrans, geom, CreateLighting(new OpenRay.Color(0.8f, 0.8f, 0.8f, 1f) ) );
			
			s.AddPrimitive( prim );
			
			//----
			
			Matrix c2w = Matrix.Inverse( Matrix.CreateFrustum(-1f, 1f, -1f, 1f, 1f, 2f) );
			OpenGLCamera camera = new OpenGLCamera(s, c2w, 500, 500 );
			
			SavePNG( camera.Render(), "TestReflectivePhongSphere.png" );
		}
	}
}

